#include "Scheduler.h"
#include "Mailer.h"
#include "iSchedulable.h"

Scheduler::Scheduler(void)
{
	m_schedulingPolicy = ROUND_ROBIN;
	m_schedulingTable = new THREADTABLE;
	m_scheduables = new map<int, iSchedulable*>();
	m_totalCPU = 0;
	maxWaitTime = 100;
}

Scheduler::~Scheduler()
{
	THREADTABLE::iterator ts;
	for(ts=m_schedulingTable->begin();ts!=m_schedulingTable->end();ts++)
	{
		delete ts->second.thread;
	}
	delete m_schedulingTable;
	delete m_scheduables;
}

void Scheduler::start()
{
	printf("Scheduler Started!!!\n");
	m_shouldRun = true;
	while(m_shouldRun)
	{
		switch(m_schedulingPolicy)
		{
			case ROUND_ROBIN:
				roundRobin();
				break;
			case LONGEST_QUEUE:
				longestQueue();
				break;
			case OUR_POLICY:
				ourPolicy();
				break;
		}
	}
	printf("Scheduler Ended!!!\n");
}

void Scheduler::stop()
{
	THREADTABLE::iterator ts;
	for(ts=m_schedulingTable->begin();ts!=m_schedulingTable->end();ts++)
	{
		ts->second.status = false;
	}
}

int Scheduler::getNodeStatus(int nodeId)
{
	return (*m_schedulingTable)[nodeId].status;		
}


void Scheduler::addThread(Thread* t, iSchedulable* sched)
{
	int id = t->getID();
	ThreadStat threadStat = {id, t, 0, 0, false};
	(*m_schedulingTable)[id] = threadStat;
	m_scheduables->insert(map<int, iSchedulable*>::value_type(id, sched));
}

void Scheduler::changePolicy(int policy)
{
	m_schedulingPolicy = policy;
}

void Scheduler::startAll()
{
	THREADTABLE::iterator ts;
	for(ts=m_schedulingTable->begin();ts!=m_schedulingTable->end();ts++)
	{
		ts->second.status = true;
	}
}

void Scheduler::run(ThreadStat* ts, int quantom)
{
	int n;

	if(ts->status)
	{
		ts->starvationTime = 0;

		n = ts->thread->run(quantom);
		if(countTime){
			ts->time += (quantom - n);
			m_totalCPU += (quantom - n);
		}

		if(n==ZOMBIE)
		{
			m_scheduables->erase(ts->ID);
			delete ts->thread;
			m_schedulingTable->erase(ts->ID);
		}
	}
}

//Scheduling Policies
void Scheduler::roundRobin()
{
	int n=0;
	THREADTABLE::iterator ts;
	m_Quantom = 20;

	for(ts=m_schedulingTable->begin();ts!=m_schedulingTable->end();ts++)
	{
		run(&ts->second, m_Quantom);
	}
}

void Scheduler::longestQueue()
{
	THREADTABLE::iterator ts;
	m_Quantom = 20;
	
	ThreadStat* nextThread = NULL;
	
	int starvingThread = -1;
	//find a starving thread if exists:
	for(ts=m_schedulingTable->begin();ts!=m_schedulingTable->end() && starvingThread == -1 ;ts++)
	{	
		if(ts->second.status && ts->second.starvationTime > maxWaitTime) starvingThread = ts->first;
	}
	if(starvingThread > -1)
	{
		
		for(ts=m_schedulingTable->begin();ts!=m_schedulingTable->end() ;ts++)
		{	
			if(ts->second.status)
			{
				if(starvingThread != ts->first)
				{
					ts->second.starvationTime++;
				}
				else nextThread = &ts->second;
			}
		}
	}
	else
	{
		//no starving threads - get longest queue
		ts=m_schedulingTable->begin();
		int maxMailSize = 0;
		while(ts!=m_schedulingTable->end())
		{
			if(ts->second.status)
			{
				int mailboxSize = m_mailer->getMailboxSize(ts->second.ID);
				if(nextThread == NULL)
				{
					nextThread = &ts->second;
					maxMailSize = mailboxSize;
				}
				else
				{
					if(mailboxSize > maxMailSize)
					{
						nextThread->starvationTime++;
						nextThread = &ts->second;
						maxMailSize = mailboxSize;
					}
					else ts->second.starvationTime++;	
				}

			}
			ts++;
		}
	}
	if(nextThread != NULL)
	{
		nextThread->starvationTime = 0;
		run(nextThread,m_Quantom);		
	}
}

void Scheduler::ourPolicy()
{
	int n=0;
	THREADTABLE::iterator ts;
//	m_Quantom = 20;
	
	for(ts=m_schedulingTable->begin();ts!=m_schedulingTable->end();ts++)
	{
		if(m_scheduables->count(ts->first) > 0)
		{
			run(&ts->second, (*m_scheduables)[ts->first]->getTimeRequest());
		}
	}
}

void Scheduler::KillAll()
{
	changePolicy(ROUND_ROBIN);
	iSchedulable* tmpShell = (*m_scheduables)[0];
	m_scheduables->clear();
	m_scheduables->insert(map<int, iSchedulable*>::value_type(0, tmpShell));
}

/*
void Scheduler::ourPolicy()
{

	THREADTABLE::iterator ts;
	m_Quantom = 1;
	
	ThreadStat* nextThread = NULL;
	int maxMailSize = 0;
	
	int starvingThread = -1;
	//find a starving thread if exists:
	for(ts=m_schedulingTable->begin();ts!=m_schedulingTable->end() && starvingThread == -1 ;ts++)
	{	
		if(ts->second.status && ts->second.starvationTime > maxWaitTime) starvingThread = ts->first;
	}
	if(starvingThread > -1)
	{
		
		for(ts=m_schedulingTable->begin();ts!=m_schedulingTable->end() ;ts++)
		{	
			if(ts->second.status)
			{
				if(starvingThread != ts->first)
				{
					ts->second.starvationTime++;
					maxMailSize = (*m_scheduables)[ts->first]->getTimeRequest();
				}
				else nextThread = &ts->second;
			}
		}
	}
	else
	{
		//no starving threads - get longest queue
		ts=m_schedulingTable->begin();
		while(ts!=m_schedulingTable->end())
		{
			if(ts->second.status)
			{
				int mailboxSize = (*m_scheduables)[ts->first]->getTimeRequest();//m_mailer->getMailboxSize(ts->second.ID);
				if(nextThread == NULL)
				{
					nextThread = &ts->second;
					maxMailSize = mailboxSize;
				}
				else
				{
					if(mailboxSize > maxMailSize)
					{
						nextThread->starvationTime++;
						nextThread = &ts->second;
						maxMailSize = mailboxSize;
					}
					else ts->second.starvationTime++;	
				}
				
			}
			ts++;
		}
	}
	if(nextThread != NULL)
	{
		nextThread->starvationTime = 0;
		run(nextThread,m_Quantom*maxMailSize);		
	}
}
*/




void Scheduler::setMailer(Mailer* m)
{
	m_mailer = m;
}


//Command Handling
int Scheduler::crashNode(int id)
{
	ThreadStat ts = (*m_schedulingTable)[id];
	if(ts.status)
	{
		(*m_schedulingTable)[id].status = false;
		return 1;
	}
	else
	{
		return 0;
	}
}

int Scheduler::reviveNode(int id)
{
	ThreadStat ts = (*m_schedulingTable)[id];
	if(!ts.status)
	{
		(*m_schedulingTable)[id].status = true;
		startCounting();
		return 1;
	}
	else
	{
		return 0;
	}
}

void Scheduler::startCounting()
{
	THREADTABLE::iterator ts;
	for(ts=m_schedulingTable->begin();ts!=m_schedulingTable->end();ts++)
	{
		ts->second.time = 0;
	}
	countTime = true;
	m_totalCPU = 0;
}

void Scheduler::stopCounting()
{
	countTime = false;
}

int Scheduler::getTotalCPU()
{
	return m_totalCPU;
}

int Scheduler::getNodeCPU(int id)
{
	return (*m_schedulingTable)[id].time;
}

void Scheduler::killCrashed()
{
	THREADTABLE::iterator ts;
	for(ts=m_schedulingTable->begin();ts!=m_schedulingTable->end();)
	{
		if(!ts->second.status)
		{
			m_scheduables->erase(ts->first);
			delete ts->second.thread;
			m_schedulingTable->erase(ts++);
		}
		else
		{
			++ts;
		}
	}
}

int Scheduler::getTTSize()
{
	return m_schedulingTable->size();
}

